//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
//------------------------------------------------------------------------------
//
//  File:  dbgserial.c            
//
//  This module is provides the interface to the serial port.
//
#include <bsp.h>
#include <nkintr.h>

#include <xllp_mfp_proc_lv.h>
#include <xllp_mfp_plat_lv.h>

#ifdef XLLP_UART
#include "xllp_uart.h"
#endif
 
//------------------------------------------------------------------------------
// Defines 
//
#define DEBUG_BAUD_38400      0x00000018
// the uart clock is 921600*16 MHz on normal mode

#define DEBUG_BAUD_38400_D0CS  0x00000013
//// the uart clock is 729600*16 MHz on D0CS mode

//------------------------------------------------------------------------------
// Externs
//
 
//------------------------------------------------------------------------------
// Global Variables 

extern BOOL g_bDebugSerialEnable;
//------------------------------------------------------------------------------
// Local Variables 
//
static volatile PXA_UART_REG    *g_pDebugUARTPort = NULL;
static BOOL gfUsePhysical = FALSE;
#ifdef XLLP_UART
	static XLLP_UART_HANDLE_T g_UartHandle;
#endif

//------------------------------------------------------------------------------
// Local Functions 
//

//------------------------------------------------------------------------------
//
//  Function: OEMInitDebugSerial
//
//  Initializes the debug serial port
//
#ifdef DMD_WORKAROUND
 extern void XScaleClearDTLB();
 extern void XScaleClearITLB();

 // DMD variables:  avoid the stack by keeping them heaped.
 static unsigned int *TTB, *pFirstDescriptor, *pSecondDescriptor, x, y;
#endif

VOID InitDebugSerial(UINT32 DbgSerPhysAddr, BOOL fUsePhysical) 
{
    UINT32 logMask = 0;
    volatile PXA_CLKMGR_REG *pCLKMGR_REGS = NULL;
    unsigned long MFP_base = 0;
  
#ifdef DMD_WORKAROUND
    // Do not run through this workaround if we're in physical mode -
    // physical mode doesn't have pagetables to walk through.
    if (!fUsePhysical)
    {
        //
        // Walk the page tables looking for error-prone descriptors
        //
        TTB = (unsigned int *)0xFFFD0000;

        //
        // We can now start perusing the 1st-level, recursing
        //   through any 2nd-level pointers we run across.
        //
        pFirstDescriptor = TTB;

        for(x=0; x<0x1000; x++, pFirstDescriptor++)
        {   //
            // use loop counter of x1000 (16KB/ 4B/W = 0x1000 == 4K)
            //

            // First examine the first level descriptor to see if it's a 2nd-level pointer
            //
            if( (*pFirstDescriptor & 0x3) == 0x1)
            {
                // We have a 2nd-level pointer.  Now go and recurse the 2nd-level PT (256 32-bit entries).
                //
                pSecondDescriptor = (unsigned int *)((*pFirstDescriptor & 0xFFFFFC00) + 0x20000000);     // PHY adx of 2nd-level PT base.  1:1 saves FOR DDR ONLY!!

                //
                // Assume is either 4K or 64K desriptor. 
                //
                for(y=0; y<256; y++, pSecondDescriptor++)
                {
                    if(*pSecondDescriptor == 0)
                        continue;                   // let's not muck with empty descriptors.

                    //*pSecondDescriptor |= 0xFF0;
                    *pSecondDescriptor &= ~0xFF0;
                    *pSecondDescriptor |= 0xAA0;
                }
            }
        }

        XScaleClearDTLB();
        XScaleClearITLB();
    }
#endif


	if(!g_bDebugSerialEnable)
	{
		// don't touch this UART as it's already exported as product serial.
		return;
	}
	
    // At this moment we must suppress logging.
    //
    logMask = g_oalLogMask;
    g_oalLogMask = 0;

	if (fUsePhysical) {
		g_pDebugUARTPort = (volatile PXA_UART_REG *) DbgSerPhysAddr;
		gfUsePhysical = TRUE;

		pCLKMGR_REGS = (volatile XLLP_CLKMGR_T *) MONAHANS_BASE_REG_PA_CLKMGR;
		MFP_base = MONAHANS_BASE_REG_PA_MFP;
	}
	else {
		g_pDebugUARTPort = (volatile PXA_UART_REG *) OALPAtoVA(DbgSerPhysAddr, FALSE);   // 0xA8100000 is the VA
		pCLKMGR_REGS = (volatile XLLP_CLKMGR_T *) OALPAtoVA(MONAHANS_BASE_REG_PA_CLKMGR, FALSE);
		MFP_base = (unsigned long) OALPAtoVA(MONAHANS_BASE_REG_PA_MFP, FALSE);
		gfUsePhysical = FALSE;
	}

#ifndef XLLP_UART
    // Ensure that UART interrupts are turned off.
    //
    g_pDebugUARTPort->lcr     = 0x0;    // Clear DLAB.
    g_pDebugUARTPort->ier_dlh = 0x0;    // IER_DLH = 0x0.

    // Set the Baud Rate (Divisor low = DEBUG_BAUD_38400).
    // The divisor latches are at offsets 0 and 1, which are 
    // receive/transmit data and ier registers.
    //
    g_pDebugUARTPort->lcr         = 0x80;                // Access Divisor.
    g_pDebugUARTPort->thr_rbr_dll = DEBUG_BAUD_38400;    // Low byte divisor.
    g_pDebugUARTPort->ier_dlh     = 0x00;                // High byte divisor.
    g_pDebugUARTPort->lcr         = 0x0;                 // Clear DLAB.

    //Setting UART properties to 8N1 
    //
    g_pDebugUARTPort->lcr         = 0x3;     // 8 bits, 1 stop, no parity. Also LCR DLAB bit = 0.

    g_pDebugUARTPort->iir_fcr     = 0x01;    // Enable the FIFO.
    g_pDebugUARTPort->iir_fcr     = 0x07;    // Clear Rx,Tx FIFOs.

    // Don't enable UART interrupts - we'll poll for the data.
    //
    g_pDebugUARTPort->ier_dlh     = 0x0;  

    // Ensure loop-back test mode is off even though MCR reset value is 0x0. 
    //
    g_pDebugUARTPort->mcr         = 0x0;    // UART is in normal mode.

    // Configure MFP pins for FFUART
    //
    // Configure MFP pins for UARTs
    //
    switch( DbgSerPhysAddr ) {
    case MONAHANS_BASE_REG_PA_FFUART:
        pCLKMGR_REGS ->d0cken_a |= XLLP_CLK_FFUART;
        /* MFP Pins */
        break;
	
    case MONAHANS_BASE_REG_PA_BTUART:
        pCLKMGR_REGS ->d0cken_a |= XLLP_CLK_BTUART;
        /* MFP Pins */
		*(XLLP_VUINT32_T*) (MFP_base + XLLP_MFP_RSVD_BT_RXD_OFFSET) = 0xA043; /* RXD */
        *(XLLP_VUINT32_T*) (MFP_base + XLLP_MFP_RSVD_BT_TXD_OFFSET) = 0xC043;
        break;
		
    case MONAHANS_BASE_REG_PA_SIRUART:
        pCLKMGR_REGS ->d0cken_a |= XLLP_CLK_STUART;
        /* MFP Pins */
        *(XLLP_VUINT32_T*) (MFP_base + XLLP_MFP_DBG_RXD_OFFSET) = 0xA041; /* RXD */
        *(XLLP_VUINT32_T*) (MFP_base + XLLP_MFP_DBG_TXD_OFFSET) = 0xC041; /* TXD */
        break;
			
    default:
        /* oooops invalid */
        return;
        break;
        ;
    };
    // Enable the UART.
    //
    g_pDebugUARTPort->ier_dlh = 0x40;

#else
	P_XLLP_UART_HANDLE_T g_pUartHandle = &g_UartHandle;
	
    // Configure an FF or BT
    if (DbgSerPhysAddr == MONAHANS_BASE_REG_PA_FFUART)
    {
        g_pUartHandle->UARTSelection = XLLP_FFUART;
	    XllpClockEnable(pCLKMGR_REGS, XLLP_CLK_FFUART, TRUE); 
    }
    else if (DbgSerPhysAddr == MONAHANS_BASE_REG_PA_BTUART)
    {
        g_pUartHandle->UARTSelection = XLLP_BTUART;
	    XllpClockEnable(pCLKMGR_REGS, XLLP_CLK_BTUART, TRUE); 
    }
	
	// Set pointer to the UART registers
	g_pUartHandle->pUARTRegs = g_pDebugUARTPort;
	
	// Set pointer to the MFP base registers
	g_pUartHandle->pMfpRegBase = ; 
	
	// Set pointer to the GPIO base registers
	g_pUartHandle->pGPIORegs = ;	
	
	// Set pointer to the resource base
	g_pUartHandle->pMfpRmDb = ;	
	
	// The resource ID of UART
	g_pUartHandle->MfpRmDBID = ;	

	// Set user specified values to write to the UART registers.
	g_pUartHandle->DLL_Value = DEBUG_BAUD_38400;	
	g_pUartHandle->DLH_Value = 0x00;	
	g_pUartHandle->LCR_Value = 0x3; // 8 bits, 1 stop, no parity. Also LCR DLAB bit = 0.	
	g_pUartHandle->FCR_Value = 0x7;	        
	g_pUartHandle->IER_Value = 0x4; // Polling	  
	g_pUartHandle->ABR_Value = 0x0;	  
	g_pUartHandle->MCR_Value = XLLP_MCR_RTS;	  
	g_pUartHandle->ISR_Value = 0x0;	  
	
	// Init UART
	XllpUartInit(g_pUartHandle);
#endif

    // Restore the logging mask.
    //
    g_oalLogMask = logMask;

//    OALMSGS(TRUE, (L"-InitDebugSerial\r\n"));
}


//------------------------------------------------------------------------------
//
//  Function: OEMWriteDebugByte
//
//  Transmits a character out the debug serial port.
//
VOID OEMWriteDebugByte(UINT8 ch) 
{
    if(!g_bDebugSerialEnable)
    {	// don't touch this UART as it's already exported as product serial.
		return;
    } 
    if (!g_pDebugUARTPort)
    {
        return;
    }
#if 0
	if (gfUsePhysical)
		g_pDebugUARTPort = (volatile PXA_UART_REG *)MONAHANS_BASE_REG_PA_FFUART;
	else
		g_pDebugUARTPort = (volatile PXA_UART_REG *) OALPAtoVA(MONAHANS_BASE_REG_PA_FFUART, FALSE);
#endif	
    // Spin if FIFO has more than half data.
    //
    while(!(g_pDebugUARTPort->lsr & 0x020));


    // Write a character byte to the FIFO.
    //
    g_pDebugUARTPort->thr_rbr_dll = (unsigned char)ch;

}


//------------------------------------------------------------------------------
//
//  Function: OEMReadDebugByte
//
//  Reads a byte from the debug serial port. Does not wait for a character. 
//  If a character is not available function returns "OEM_DEBUG_READ_NODATA".
//
int OEMReadDebugByte() 
{
    UINT32 data = OEM_DEBUG_READ_NODATA;
    UINT32 LSR;

    if(!g_bDebugSerialEnable)
    {	// don't touch this UART as it's already exported as product serial.
	return(OEM_DEBUG_COM_ERROR);
    }

    if (!g_pDebugUARTPort)
    {
        return(data);
    }	
#if 0
	if (gfUsePhysical)
		g_pDebugUARTPort = (volatile PXA_UART_REG *) MONAHANS_BASE_REG_PA_FFUART;
	else
		g_pDebugUARTPort = (volatile PXA_UART_REG *) OALPAtoVA(MONAHANS_BASE_REG_PA_FFUART, FALSE);
#endif

    // Read LSR.
    //
    LSR = g_pDebugUARTPort->lsr;

    // Return if no data.
    //
    if(!(LSR & 0x1))
    {
        return(data);
    }

    // Read data.
    //
    data = g_pDebugUARTPort->thr_rbr_dll;

    // Signal error if PE or FE was set.
    // Do nothing if BI or OE was set.
    //
    if(LSR & 0x0c)
    {
        data = OEM_DEBUG_COM_ERROR;
    }

    return(data);

}


//------------------------------------------------------------------------------
//
//  Function: //OEMWriteDebugLED
//
//  Writes a pattern to the on-board hex LEDs.  *this needs to be changed...
//

void OEMWriteDebugLED(UINT16 index, DWORD pattern)
{
#if 0
#define HEXLED_PHYSICAL_BASE 		0x14200000 //0x40E00000
#define SW2_POSITION                1           // candidate value is 1,2,3
    volatile UINT32 *pHEXLED;

	if (gfUsePhysical)
		pHEXLED = (UINT32*)HEXLED_PHYSICAL_BASE;
	else
		pHEXLED = (UINT32*)OALPAtoVA(HEXLED_PHYSICAL_BASE, FALSE);

	if((index==0) && (pattern <= 0xF))
	{
        // HEX SW2 of CF and Ethernet card affects the LED display.
        // if it's is set to "1", the pattern must shift left 4 bits to make LED usable.
        // For details please check CPLD V1.04 for CF and Ethernet card.
        // Setting SW2 to "0" should be avoided because it triggers additional I2C transactions
		*pHEXLED = pattern<<(4*SW2_POSITION); 
	}
#endif	
}

void OEMWriteDebugLEDFlashing(UINT32 time)
{
#if 0
	static UINT32 ledCount = 0;
	static BOOL ledFlagON = 0;
	static BOOL ledFlagOFF = 1;

    if ((ledCount < time) && (ledFlagON == 1))
	{
    	OEMWriteDebugLED(0, 1);
	}
    else if ((ledCount < time) && (ledFlagOFF == 1))
	{
    	OEMWriteDebugLED(0, 0);
	}
	
    if ((ledCount == time) && (ledFlagON == 1))
	{
		ledCount = 0;
		ledFlagON = 0;
		ledFlagOFF = 1;
	}
    else if ((ledCount == time) && (ledFlagOFF == 1))
	{
		ledCount = 0;
		ledFlagON = 1;
		ledFlagOFF = 0;
	}
	ledCount++;
#endif	
}

//------------------------------------------------------------------------------
//
//  Function: OEMClearDebugCommError
//
//  Clears communications errors (flushes the serial FIFO).
//
void OEMClearDebugCommError(void) 
{
    while(OEMReadDebugByte() == OEM_DEBUG_COM_ERROR);
}

//------------------------------------------------------------------------------
